<?php

namespace App\Services\Traits;

use Helper\Timed;
use Ramsey\Uuid\Type\Time;

trait DatabaseElasticsearch
{

    /**
     * 制作排序参数
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @param string $column
     * @param string $order
     * @return $this
     */
    protected function makeOrderParam($request, &$params, $column = null, $order = null)
    {
        $params['body']['sort'][] = [
            $column ?: $request->input('order_column', '_id') => [
                'order' => $order ?: $request->input('order', 'desc')
            ]
        ];
        return $this;
    }

    /**
     * 制作分页参数
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @return $this
     */
    protected function makePaginationParam($request, &$params)
    {
        $params['body']['from'] = ($this->getPage($request) - 1) * $this->getPageSize($request);
        $params['body']['size'] = $this->getPageSize($request);

        return $this;
    }

    /**
     * 制作查询
     *
     * @param array $params
     * @param string $column
     * @param mixed $value
     * @param string $operator
     * @return $this
     */
    protected function makeWhere(&$params, $column = null, $value = null, $operator = 'term')
    {
        $params['body']['query']['bool']['must'][] = [
            is_array($value) ? 'terms' : $operator => [
                $column => $value
            ]
        ];
        return $this;
    }

    /**
     * 制作查询【排除】
     *
     * @param array $params
     * @param string $column
     * @param mixed $value
     * @param string $operator
     * @return $this
     */
    protected function makeWhereNot(&$params, $column = null, $value = null, $operator = 'term')
    {
        $params['body']['query']['bool']['must_not'][] = [
            is_array($value) ? 'terms' : $operator => [
                $column => $value
            ]
        ];
        return $this;
    }

    /**
     * 制作查询参数
     *
     * @param \Illuminate\Http\Request $request
     * @param string $key
     * @param array $params
     * @param string $column
     * @param string $operator
     * @return $this
     */
    protected function makeWhereParam($request, $key, &$params, $column = null, $operator = 'term')
    {
        if ($this->isValidParam($request, $key)) {
            $this->makeWhere($params, $column ?: $key, $this->getParam($request, $key), $operator);
        }
        return $this;
    }

    protected function makeWhereParamAndNull($request, $key, &$params, $column = null, $operator = 'term')
    {
        if ($this->isValidParam($request, $key)) {
            $this->makeWhereAndNull($params, $column ?: $key, $this->getParam($request, $key), $operator);
        }
        return $this;
    }

    protected function makeWhereAndNull(&$params, $column = null, $value = null, $operator = 'term')
    {
//        if((in_array($column, ['ext.is_free', 'ext.is_free_trigger']) && $value == 0) || in_array($column, ['ext.is_bonus', 'ext.is_bonus_trigger']) && $value == 1){
        if($value == 0){
            $params['body']['query']['bool']['must'][] = [
                'bool' => [
                    'minimum_should_match' => 1,
                    'should' => [
                        [
                            'bool' => [
                                "must_not" => [
                                    'exists' => [
                                        'field' => $column,
                                    ],
                                ],
                            ]
                        ],
                        [
                            $operator => [$column => $value]
                        ],
                    ],
                ]
            ];
        }else{
            $params['body']['query']['bool']['must'][] = [
                is_array($value) ? 'terms' : $operator => [
                    $column => $value
                ]
            ];
        }


        return $this;
    }

    /**
     * 制作查询参数【排除】
     *
     * @param \Illuminate\Http\Request $request
     * @param string $key
     * @param array $params
     * @param string $column
     * @param string $operator
     * @return $this
     */
    protected function makeWhereParamNot($request, $key, &$params, $column = null, $operator = 'term')
    {
        if ($this->isValidParam($request, $key)) {
            $this->makeWhereNot($params, $column ?: $key, $this->getParam($request, $key), $operator);
        }
        return $this;
    }

    /**
     * 制作查询参数【搜索】
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @param string $column
     * @param string $key
     * @return $this
     */
    protected function makeWhereParamSearch($request, &$params, $column = null, $key = 'keyword')
    {
        if ($this->isValidParam($request, $key)) {

            $column = $column ?: $request->input('keyword_column', 'id');

            $this->makeWhereParam($request, $key, $params, $column);
        }
        return $this;
    }

    /**
     * 制作查询参数【datetime】
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @param null $column
     * @param string $key
     * @param string|null $index
     * @return $this
     */
    protected function makeWhereParamDatetime($request, &$params, $column = null, $key = 'datetime', $index = null, $start_time='',
	    $end_time='', $type='day', $add = true,  $is_time=true)
    {
        if ($this->isValidParam($request, $key)) {

            $datetime = $this->getParamArray($request, $key);

            $column = $column ?: $request->input('datetime_column', 'created');
	        $is_time && list($start_time, $end_time) = Timed::datetimeFormat($request,'datetime', $start_time, $end_time, $type, $add);

	        $params['body']['query']['bool']['must'][] = [
                'range' => [
                    $column => [
	                    'gte' => $start_time,
	                    'lte' => $end_time
                    ]
                ]
            ];

	        // 设置索引范围
	        if (!empty($index) && ($end_time - $start_time) / 86400 <= 31) {
		        // 忽略不存在索引
		        $params['ignore_unavailable'] = true;
		        // 枚举按天分表索引（前后冗余各1天，排除时区误差）
		        for ($timed = $start_time - 2*86400; $timed <= $end_time + 2*86400; $timed += 86400) {
			        $day = date('Y.m.d', $timed);
			        $params['index'][] = "{$index}-{$day}";
		        }
	        }
        }
        return $this;
    }

	protected function makeWhereParamDatetime1($request, &$params, $column = null, $key = 'datetime', $index = null, $start_time='',
		$end_time='', $type='day', $add = true,  $is_time=true)
	{
		if ($this->isValidParam($request, $key)) {

			$datetime = $this->getParamArray($request, $key);

			$column = $column ?: $request->input('datetime_column', 'created');
			$is_time && list($start_time, $end_time) = Timed::datetimeFormat1($request,'datetime', $start_time, $end_time, $type, $add);

			$params['body']['query']['bool']['must'][] = [
				'range' => [
					$column => [
						'gte' => $start_time,
						'lte' => $end_time
					]
				]
			];

			// 设置索引范围
			if (!empty($index) && ($end_time - $start_time) / 86400 <= 31) {
				// 忽略不存在索引
				$params['ignore_unavailable'] = true;
				// 枚举按天分表索引（前后冗余各1天，排除时区误差）
				for ($timed = $start_time - 2*86400; $timed <= $end_time + 2*86400; $timed += 86400) {
					$day = date('Y.m.d', $timed);
					$params['index'][] = "{$index}-{$day}";
				}
			}
		}
		return $this;
	}

    /**
     * 制作查询参数【最近n天】
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @param string $column
     * @param int $days
     * @return $this
     */
    protected function makeWhereLastDays($request, &$params, $column = null, $days = 30, $logic = false)
    {
		$dateTime = $request->input('datetime', []);
		if ($logic) {
			if (!isset($dateTime[1])) {
				$endSearchTime = Timed::getDayNextTimed() - 1;
			} else {
				$endSearchTime = $dateTime[1];
			}
		} else {
			$endSearchTime = Timed::getDayNextTimed() - 1;
		}

        $request->offsetSet('datetime', [Timed::getTodayTimed() - 86400 * ($days - 1), $endSearchTime]);

        $this->makeWhereParamDatetime($request, $params, $column, 'datetime');

        return $this;
    }

	protected function makeWhereLastDays1($request, &$params, $column = null, $days = 30, $logic = false)
	{
		$dateTime = $request->input('datetime', []);
		$endSearchTime = Timed::getDayNextTimed() - 1;

		$request->offsetSet('datetime', [Timed::getTodayTimed() - 86400 * ($days - 1), $endSearchTime]);
		$startTime = Timed::getTodayTimed() - 86400 * ($days - 1);
		$endTime = Timed::getDayNextTimed() - 1;
		$this->makeWhereParamDatetime1($request, $params, $column, 'datetime', null, $startTime, $endTime);

		return $this;
	}

    /**
     * 制作统计参数
     *
     * @param array $params
     * @param string $column
     * @param string $agg
     * @param string $name
     * @return $this
     */
    protected function makeAgg(&$params, $column, $agg, $name = null)
    {
        $params['body']['aggs'][$name ?: $column] = [
            $agg => [
                'field' => $column
            ]
        ];
        return $this;
    }

    /**
     * 制作统计参数【多条】
     *
     * @param array $params
     * @param string $agg
     * @param array $columns
     * @return $this
     */
    protected function makeAggs(&$params, $agg, $columns)
    {
        foreach ($columns as $column) {
            $this->makeAgg($params, $column, $agg);
        }
        return $this;
    }

    /**
     * 制作统计参数【分组】【多条】
     *
     * @param array $params
     * @param string $agg
     * @param array $columns
     * @param array $groups
     * @return $this
     */
    // protected function makeGroupAggs(&$params, $agg, $columns, $groups)
    // {
    //     $params['body']['aggs']['group'] = [
    //         'composite' => [
    //             'size' => config('elasticsearch.search.max_buckets'),
    //             'sources' => array_map(function ($group) {
    //                 return is_array($group) ? $group : [$group => ['terms' => ['field' => $group]]];
    //             }, $groups)
    //         ],
    //         'aggs' => array_merge(...array_map(
    //             function ($column) use ($agg) {
    //                 return [
    //                     $column => [
    //                         $agg => [
    //                             'field' => $column
    //                         ]
    //                     ]
    //                 ];
    //             },
    //             $columns
    //         ))
    //     ];
    //     return $this;
    // }
	protected function makeGroupAggs($request, &$params, $agg, $columns, $groups, $group_time = '', $key = 'datetime', $field_rec='.')
	{
		$aggArr = [
			'composite' => [
				'size' => config('elasticsearch.search.max_buckets'),
				'sources' => array_map(function ($group) {
					return is_array($group) ? $group : [$group => ['terms' => ['field' => $group]]];
				}, $groups)
			],
		];

		$datetime = $this->getParamArray($request, $key);
		if (!isset($datetime[0])) {
			$datetime[0] = strtotime('-90 days');
			$datetime[1] = time();
		}

		// 1.获取本地时区时间搓
		list($start_time, $end_time) = Timed::datetimeFormat($request,'datetime', $datetime[0], $datetime[1]);

		if($group_time){
			$aggArr['aggs'][$group_time]['date_histogram'] = [
				'field' => 'end_time_at',
				'time_zone' => Timed::getTimeCode(),
				'calendar_interval' => '1d',
				'format' => 'yyyy-MM-dd',
				'min_doc_count' => 0,
				'extended_bounds' => [
					'min' => $start_time * 1000,
					'max' => $end_time * 1000,
				],
			];

			$aggArr['aggs'][$group_time]['aggs'] = array_merge(...array_map(
				function ($column) use ($agg) {
					return [
						$column => [
							$agg => [
								'field' => $column
							]
						]
					];
				},
				$columns
			));
		}else{
			$aggArr['aggs'] = array_merge(...array_map(
				function ($column) use ($agg, $field_rec) {
					return [
						str_replace('.', $field_rec, $column) => [
							$agg => [
								'field' => $column
							]
						]
					];
				},
				$columns
			));
		}

		$params['body']['aggs']['group'] = $aggArr;

		return $this;
	}

    /**
     * 制作查询参数【平台】
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @param string $column
     * @return $this
     */
    protected function makeWhereOrg($request, &$params, $column = 'plat_code')
    {
        $this->makeWhere($params, $column, $request->user()->getPermissionOrgCodes());

        return $this;
    }

    /**
     * 制作查询参数【平台】【默认值】
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @param string $column
     * @param string $key
     * @return $this
     */
    protected function makeWhereParamOrg($request, &$params, $column = 'plat_code', $key = 'plat_code')
    {
        if ($this->isValidParam($request, $key)) {
            $this->makeWhere($params, $column, $this->getParam($request, $key));
        } else {
            $this->makeWhereOrg($request, $params, $column);
        }
        return $this;
    }

    /**
     * 制作查询参数【自定义字段范围筛选】
     *
     * @param \Illuminate\Http\Request $request
     * @param array $params
     * @param string $key
     * @return $this
     */
    protected function makeWhereFieldRange($request, &$params, $key = 'fieldRange')
    {
        if ($this->isValidParam($request, $key)) {
            $option = $this->getParamArray($request, $key);

            if (empty($option['field'])) {
                return $this;
            }

            if (isset($option['min']) && !is_bool($option['min']) && trim((string)$option['min']) !== '') {
                $range['gte'] = $option['min'];
            }

            if (isset($option['max']) && !is_bool($option['max']) && trim((string)$option['max']) !== '') {
                $range['lte'] = $option['max'];
            }

            if (isset($range)) {
                $params['body']['query']['bool']['must'][] = [
                    'range' => [
                        $option['field'] => $range
                    ]
                ];
            }
        }
        return $this;
    }

	protected function makeAggsOrderParam($request, &$params, $column = null, $order = null)
	{
		$order_column = $request->input('order_column', '');
		if(!empty($order_column)){
			$params['body']['aggs']['group']['aggs']['sales_bucket_sort']['bucket_sort']['sort'] = [$column ? $column : $request->input('order_column', '_id') => ['order' => $request->input('order', 'desc')]];
		}

		return $this;
	}

	protected function makeAggsPaginationParam($request, &$params)
	{
		$order_column = $request->input('order_column', '');
		if(!empty($order_column)){
			$params['body']['aggs']['group']['aggs']['sales_bucket_sort']['bucket_sort']['from'] = ($this->getPage($request) - 1) * $this->getPageSize($request);
			$params['body']['aggs']['group']['aggs']['sales_bucket_sort']['bucket_sort']['size'] = $this->getPageSize($request);
		}

		return $this;
	}

	/**
	 * 制作统计参数【去重计数】
	 *
	 * @param string $column
	 * @param string|null $name
	 * @param int $threshold
	 * @return array
	 */
	protected function makeDistinct(string $column, ?string $name = null, int $threshold = 40000)
	{
		return [
			$name ?: "distinct_{$column}" => [
				'cardinality' => [
					'field' => $column,
					'precision_threshold' => $threshold
				]
			]
		];
	}

	protected function makeGroupDatetimeAggs($request, &$params, $agg, $columns, $groups, $group_str, $group_time_type = '1d', $timed_key='end_time_at', $conn = '_')
	{
		$groupStr = '';
		foreach ($groups as $group){
			$groupStr .= (empty($groupStr) ? '' : '+' ). " doc['{$group}'].value + '$conn' ";
		}

		$aggArr = [
			'composite' => [
				'size' => config('elasticsearch.search.max_buckets'),
				'sources' => [
					[
						"timed" => [
							"date_histogram" => [
								"field" => $timed_key,
								"calendar_interval" => $group_time_type,
								"time_zone" =>Timed::getTimeCode(),
							]
						],
					],
				],
			],
		];

		if(!empty($groupStr)){
			$aggArr['composite']['sources'][] = [
				$group_str => [
					"terms" => [
						"script" => [
							'source' => $groupStr,
							'lang' => "painless",
						]
					]
				],
			];
		}


		$aggArr['aggs'] = array_merge(...array_map(
			function ($column) use ($agg) {
				return [
					$column => [
						$agg => [
							'field' => $column
						]
					]
				];
			},
			$columns
		));

		$params['body']['aggs']['group'] = $aggArr;

		return $this;
	}

	/**
	 * 制作统计参数【分组】
	 *
	 * @param array $columns
	 * @param array|null $aggs
	 * @return array
	 */
	protected function makeGroup(array $columns, ?array $aggs = null)
	{
		return [
			'group' => [
				'composite' => [
					'size' => config('elasticsearch.search.max_buckets'),
					'sources' => array_map(function ($column) {
						return is_array($column) ? $column : [$column => ['terms' => ['field' => $column]]];
					}, $columns)
				],
				'aggregations' => empty($aggs) ? new \stdClass() : $aggs
			]
		];
	}
}
